package com.wande.dataplatform.filecollection.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wande.common.core.utils.MapstructUtils;
import com.wande.common.mybatis.core.page.PageQuery;
import com.wande.common.mybatis.core.page.TableDataInfo;
import com.wande.dataplatform.filecollection.common.constant.FileCollectionConstants;
import com.wande.dataplatform.filecollection.common.enums.FileCollectionErrorCode;
import com.wande.dataplatform.filecollection.common.exception.FileCollectionException;
import com.wande.dataplatform.filecollection.common.utils.FileTypeUtils;
import com.wande.dataplatform.filecollection.common.utils.MinioUtils;
import com.wande.dataplatform.filecollection.domain.UnstructuredFile;
import com.wande.dataplatform.filecollection.domain.bo.UnstructuredFileBo;
import com.wande.dataplatform.filecollection.domain.vo.UnstructuredFileVo;
import com.wande.dataplatform.filecollection.mapper.UnstructuredFileMapper;
import com.wande.dataplatform.filecollection.service.IUnstructuredFileService;
import com.wande.dataplatform.filecollection.source.IFileSource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 非结构化文件服务实现
 *
 * @author wande
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UnstructuredFileServiceImpl implements IUnstructuredFileService {

    private final UnstructuredFileMapper unstructuredFileMapper;
    private final MinioUtils minioUtils;
    private final Map<String, IFileSource> fileSourceMap;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long uploadFile(MultipartFile file, UnstructuredFileBo bo) {
        try {
            // 验证文件
            validateFile(file);

            // 计算文件MD5
            String md5 = calculateMd5(file);

            // 检查文件是否已存在
            UnstructuredFile existingFile = checkFileExists(md5);
            if (existingFile != null) {
                log.info("文件已存在，MD5: {}", md5);
                return existingFile.getId();
            }

            // 生成OSS路径
            String ossPath = generateOssPath(file.getOriginalFilename());

            // 上传到MinIO
            String uploadedPath = minioUtils.uploadFile(file.getInputStream(), file.getOriginalFilename(), 
                file.getContentType(), false);

            // 提取元数据
            Map<String, Object> metadata = extractMetadata(file);

            // 保存文件记录
            UnstructuredFile entity = new UnstructuredFile();
            entity.setFileName(file.getOriginalFilename());
            entity.setFileType(FileTypeUtils.getFileType(file.getOriginalFilename()));
            entity.setFileSize(file.getSize());
            entity.setFileMd5(md5);
            entity.setOssPath(uploadedPath);
            entity.setUploadType(bo.getUploadType() != null ? bo.getUploadType() : "MANUAL");
            entity.setSourceType(bo.getSourceType());
            entity.setSourceConfig(bo.getSourceConfig());
            entity.setMetadata(BeanUtil.beanToMap(metadata).toString());
            entity.setRemark(bo.getRemark());

            unstructuredFileMapper.insert(entity);

            log.info("文件上传成功，ID: {}, 文件名: {}", entity.getId(), entity.getFileName());
            return entity.getId();

        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_UPLOAD_FAILED, e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> uploadFiles(List<MultipartFile> files, UnstructuredFileBo bo) {
        List<Long> fileIds = new ArrayList<>();
        for (MultipartFile file : files) {
            try {
                Long fileId = uploadFile(file, bo);
                fileIds.add(fileId);
            } catch (Exception e) {
                log.error("批量上传文件失败，文件名: {}", file.getOriginalFilename(), e);
                // 继续处理其他文件
            }
        }
        return fileIds;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void collectFiles(String sourceType, String sourceConfig) {
        try {
            // 获取文件源
            IFileSource fileSource = fileSourceMap.get(sourceType.toLowerCase() + "FileSource");
            if (fileSource == null) {
                throw new FileCollectionException(FileCollectionErrorCode.UNSUPPORTED_SOURCE_TYPE,
                    "不支持的来源类型: " + sourceType);
            }

            // 获取文件列表
            List<String> filePaths = fileSource.listFiles(sourceConfig);

            log.info("从{}采集到{}个文件", sourceType, filePaths.size());

            // 逐个采集文件
            for (String filePath : filePaths) {
                try {
                    collectSingleFile(fileSource, sourceConfig, filePath, sourceType);
                } catch (Exception e) {
                    log.error("采集文件失败，路径: {}", filePath, e);
                    // 继续处理其他文件
                }
            }

        } catch (Exception e) {
            log.error("文件采集失败", e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_COLLECTION_FAILED, e.getMessage());
        }
    }

    /**
     * 采集单个文件
     */
    private void collectSingleFile(IFileSource fileSource, String sourceConfig, String filePath, String sourceType) {
        try (InputStream inputStream = fileSource.getFileStream(sourceConfig, filePath)) {
            // 计算MD5
            byte[] fileBytes = inputStream.readAllBytes();
            String md5 = DigestUtil.md5Hex(fileBytes);

            // 检查文件是否已存在
            UnstructuredFile existingFile = checkFileExists(md5);
            if (existingFile != null) {
                log.info("文件已存在，跳过采集，路径: {}", filePath);
                return;
            }

            // 生成OSS路径
            String fileName = FileUtil.getName(filePath);
            String ossPath = generateOssPath(fileName);

            // 上传到MinIO
            String uploadedPath = minioUtils.uploadFile(new java.io.ByteArrayInputStream(fileBytes), 
                fileName, "application/octet-stream", false);

            // 保存文件记录
            UnstructuredFile entity = new UnstructuredFile();
            entity.setFileName(fileName);
            entity.setFileType(FileTypeUtils.getFileType(fileName));
            entity.setFileSize((long) fileBytes.length);
            entity.setFileMd5(md5);
            entity.setOssPath(uploadedPath);
            entity.setUploadType("AUTO");
            entity.setSourceType(sourceType);
            entity.setSourceConfig(sourceConfig);

            unstructuredFileMapper.insert(entity);

            log.info("文件采集成功，ID: {}, 文件名: {}", entity.getId(), entity.getFileName());

        } catch (Exception e) {
            log.error("采集单个文件失败，路径: {}", filePath, e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_COLLECTION_FAILED, e.getMessage());
        }
    }

    @Override
    public UnstructuredFileVo getFile(Long id) {
        UnstructuredFile entity = unstructuredFileMapper.selectById(id);
        if (entity == null) {
            throw new FileCollectionException(FileCollectionErrorCode.FILE_NOT_FOUND, "文件不存在");
        }

        UnstructuredFileVo vo = MapstructUtils.convert(entity, UnstructuredFileVo.class);
        vo.setFileSizeFormatted(formatFileSize(entity.getFileSize()));
        return vo;
    }

    @Override
    public TableDataInfo<UnstructuredFileVo> listFiles(UnstructuredFileBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<UnstructuredFile> wrapper = buildQueryWrapper(bo);
        Page<UnstructuredFileVo> page = unstructuredFileMapper.selectVoPage(pageQuery.build(), wrapper);

        // 格式化文件大小
        page.getRecords().forEach(vo -> vo.setFileSizeFormatted(formatFileSize(vo.getFileSize())));

        return TableDataInfo.build(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFile(Long id) {
        UnstructuredFile entity = unstructuredFileMapper.selectById(id);
        if (entity == null) {
            throw new FileCollectionException(FileCollectionErrorCode.FILE_NOT_FOUND, "文件不存在");
        }

        try {
            // 删除MinIO中的文件
            minioUtils.deleteFile(entity.getOssPath());

            // 删除数据库记录
            unstructuredFileMapper.deleteById(id);

            log.info("文件删除成功，ID: {}, 文件名: {}", id, entity.getFileName());

        } catch (Exception e) {
            log.error("文件删除失败，ID: {}", id, e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_DELETE_FAILED, e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFiles(List<Long> ids) {
        for (Long id : ids) {
            try {
                deleteFile(id);
            } catch (Exception e) {
                log.error("批量删除文件失败，ID: {}", id, e);
                // 继续处理其他文件
            }
        }
    }

    @Override
    public String getDownloadUrl(Long id) {
        UnstructuredFile entity = unstructuredFileMapper.selectById(id);
        if (entity == null) {
            throw new FileCollectionException(FileCollectionErrorCode.FILE_NOT_FOUND, "文件不存在");
        }

        try {
            // 生成预签名URL，有效期7天
            return minioUtils.getFileUrl(entity.getOssPath(), 7 * 24 * 60 * 60);
        } catch (Exception e) {
            log.error("生成下载URL失败，ID: {}", id, e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_DOWNLOAD_FAILED, e.getMessage());
        }
    }

    @Override
    public Map<String, Object> extractMetadata(MultipartFile file) {
        Map<String, Object> metadata = new HashMap<>();

        try {
            metadata.put("originalFilename", file.getOriginalFilename());
            metadata.put("contentType", file.getContentType());
            metadata.put("size", file.getSize());
            metadata.put("extension", FileUtil.extName(file.getOriginalFilename()));
            metadata.put("uploadTime", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

            // 可以根据文件类型提取更多元数据
            // 例如：图片的宽高、文档的页数等

        } catch (Exception e) {
            log.error("提取文件元数据失败", e);
        }

        return metadata;
    }

    /**
     * 验证文件
     */
    private void validateFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new FileCollectionException(FileCollectionErrorCode.FILE_EMPTY, "文件为空");
        }

        // 验证文件大小（默认最大500MB）
        long maxSize = 500 * 1024 * 1024L;
        if (file.getSize() > maxSize) {
            throw new FileCollectionException(FileCollectionErrorCode.FILE_SIZE_EXCEEDED,
                "文件大小超过限制: " + maxSize);
        }

        // 验证文件类型
        String fileType = FileTypeUtils.getFileType(file.getOriginalFilename());
        if (StrUtil.isBlank(fileType)) {
            throw new FileCollectionException(FileCollectionErrorCode.UNSUPPORTED_FILE_TYPE,
                "不支持的文件类型");
        }
    }

    /**
     * 计算文件MD5
     */
    private String calculateMd5(MultipartFile file) {
        try {
            return DigestUtil.md5Hex(file.getInputStream());
        } catch (Exception e) {
            log.error("计算文件MD5失败", e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_MD5_FAILED, "计算文件MD5失败");
        }
    }

    /**
     * 检查文件是否已存在
     */
    private UnstructuredFile checkFileExists(String md5) {
        LambdaQueryWrapper<UnstructuredFile> wrapper = Wrappers.<UnstructuredFile>lambdaQuery();
        wrapper.eq(UnstructuredFile::getFileMd5, md5);
        return unstructuredFileMapper.selectOne(wrapper);
    }

    /**
     * 生成OSS路径
     */
    private String generateOssPath(String fileName) {
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String extension = FileUtil.extName(fileName);
        return String.format("unstructured/%s/%s.%s", date, uuid, extension);
    }

    /**
     * 格式化文件大小
     */
    private String formatFileSize(Long size) {
        if (size == null) {
            return "0 B";
        }

        if (size < 1024) {
            return size + " B";
        } else if (size < 1024 * 1024) {
            return String.format("%.2f KB", size / 1024.0);
        } else if (size < 1024 * 1024 * 1024) {
            return String.format("%.2f MB", size / (1024.0 * 1024));
        } else {
            return String.format("%.2f GB", size / (1024.0 * 1024 * 1024));
        }
    }

    /**
     * 构建查询条件
     */
    private LambdaQueryWrapper<UnstructuredFile> buildQueryWrapper(UnstructuredFileBo bo) {
        LambdaQueryWrapper<UnstructuredFile> wrapper = Wrappers.<UnstructuredFile>lambdaQuery();

        wrapper.like(StrUtil.isNotBlank(bo.getFileName()), UnstructuredFile::getFileName, bo.getFileName())
            .eq(StrUtil.isNotBlank(bo.getFileType()), UnstructuredFile::getFileType, bo.getFileType())
            .eq(StrUtil.isNotBlank(bo.getUploadType()), UnstructuredFile::getUploadType, bo.getUploadType())
            .eq(StrUtil.isNotBlank(bo.getSourceType()), UnstructuredFile::getSourceType, bo.getSourceType())
            .orderByDesc(UnstructuredFile::getCreateTime);

        return wrapper;
    }
}
